package com.example.threadmodel;

import com.example.threadmodel.model.Action;
import com.example.threadmodel.model.StudentWithExpensiveOperation;
import com.example.threadmodel.service.HashThreadManagerService;
import com.example.threadmodel.service.ThreadManagerService;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.concurrent.*;

/*
结论：如果里面有复杂的操作(比如：耗时10ms以上的任务)，映射到一个队列上面了，则耗时会比较久  Queue比绑定Thread快

缓存cache miss之类的比起来，BindQueue模式会更快

思考：
    如果是有昂贵操作(rpc、db)，那绑定队列更好一些.    如果是：只有内存，那绑定线程更快!!!
    解决的问题，分块加载, 提供更统一的操作!!!
 */
@Slf4j
@SpringBootTest
class Demo5_ManyPlayerQueueVsBindThreadWithExpensiveTask {
    @Autowired
    private ThreadManagerService threadManagerService;

    @Autowired
    private HashThreadManagerService hashThreadManagerService;

    /*
    2025-08-31 21:33:13 [INFO ] [main] c.e.t.Demo6_ManyPlayerWithExpensive.testQueue(Demo6_ManyPlayerWithExpensive.java:62) - costTime=16126
     */
    @Test
    void testQueue() throws Exception {
        int studentNum = 320;
        int execCount = 100;

        CountDownLatch countDownLatch = new CountDownLatch(studentNum * execCount);

        ConcurrentHashMap<String, StudentWithExpensiveOperation> studentMap = new ConcurrentHashMap<>();
        for (int i = 0; i < studentNum; i++) {
            studentMap.put(i + "", new StudentWithExpensiveOperation(i + "", 0));
        }

        long startTime = System.currentTimeMillis();

        for (int c = 0; c < studentNum; c++) {
            StudentWithExpensiveOperation student = studentMap.get(c + "");

            // 每人执行1W次任务
            for (int i = 0; i < execCount; i++) {
                threadManagerService.enqueueAction(new Action<Integer>(student.getId(), () -> {
                    student.doExpensiveLogic(student.getCoinCount() + 1);

                    countDownLatch.countDown();
                    return 0;
                }));
            }
        }

        // 等待确保执行完毕
        countDownLatch.await();

        long costTime = System.currentTimeMillis() - startTime;

        for (int c = 0; c < studentNum; c++) {
            StudentWithExpensiveOperation student = studentMap.get(c + "");
            //log.info("student:{}", student);
        }

        log.info("costTime={}", costTime);

        TimeUnit.SECONDS.sleep(Long.MAX_VALUE);
    }

    /*
    2025-08-31 21:34:11 [WARN ] [main] c.e.t.Demo6_ManyPlayerWithExpensive.testThread(Demo6_ManyPlayerWithExpensive.java:108) - costTime=31208
     */
    @Test
    void testThread() throws Exception {
        int studentNum = 320;
        int execCount = 100;

        CountDownLatch countDownLatch = new CountDownLatch(studentNum * execCount);

        ConcurrentHashMap<String, StudentWithExpensiveOperation> studentMap = new ConcurrentHashMap<>();
        for (int i = 0; i < studentNum; i++) {
            studentMap.put(i + "", new StudentWithExpensiveOperation(i + "", 0));
        }

        long startTime = System.currentTimeMillis();

        // 1000个学生
        for (int c = 0; c < studentNum; c++) {
            StudentWithExpensiveOperation student = studentMap.get(c + "");

            // 每人执行1W次任务
            for (int i = 0; i < execCount; i++) {
                hashThreadManagerService.addTask(student.getId().hashCode(), () -> {
                    student.doExpensiveLogic(student.getCoinCount() + 1);

                    countDownLatch.countDown();
                });
            }
        }

        // 等待3s，确保执行完毕
        countDownLatch.await();

        long costTime = System.currentTimeMillis() - startTime;

        for (int c = 0; c < studentNum; c++) {
            StudentWithExpensiveOperation student = studentMap.get(c + "");
            //log.info("student:{}", student);
        }

        log.warn("costTime={}", costTime);

        TimeUnit.SECONDS.sleep(Long.MAX_VALUE);
    }
}
